/*
 * spi.c
 *
 *  Created on: Sep 30, 2013
 *      Author: Ken Arok
 */


#include "config_board.h"

#if BOARD_2_1_USED

#include <avr/io.h>
#include "gpio.h"
#include "timers_hw.h"
#include "spi.h"


#define SPI_SELECT_MODE_MASK	0xF3
#define SPI_MODE0_BIT_MASK		0x00
#define SPI_MODE1_BIT_MASK		0x04
#define SPI_MODE2_BIT_MASK		0x08
#define SPI_MODE3_BIT_MASK		0x0C



//! \internal Interrupt flag utilities
static bool spi_interrupt_is_used = true;

//! \internal SPI master/slave mode flag
static bool spi_is_master = false;

//! \internal Pointer to the data byte transmit buffer.
static const volatile uint8_t  *spi_tx_data_byte = NULL;

//! \internal Pointer to the data byte receive buffer.
static volatile uint8_t *spi_rx_data_byte = NULL;

//! \internal Remaining number of data to transmit.
static volatile uint32_t spi_tx_nb_count = 0;

//! \internal Total number of data to receive.
static volatile uint32_t spi_rx_nb_received = 0;

//! \internal Remaining number of data to receive.
static volatile uint32_t spi_rx_nb_count = 0;

//! \internal Write direction data.
static bool operation_is_write = false;

/* Internal signaling for transmitter receiver. */
static volatile bool xSPISemaphore = true;

/* Global signaling to wake up higher task. */
volatile bool xSPISignaling = false;

/* Flag to indicate waiting frame. */
static volatile bool spi_wait_recv_frame = false;

/* Receive timeout value in milisecond. */
static volatile uint32_t *_tmr_recv_spi = NULL;
static uint32_t receive_timeout;

/* Function of received signaling. */
static TimerFuncVoid_t _funcRecvSignaling;


/** \brief SPI Interrupt Handler.
 *
 */
static void spi_interrupt_handler(void);

/** \brief Initialize SPI.
 *
 */
static void spi_init(void);

/** \brief Start transfer data into SPI peripheral.
 *
 * \param _ptrData	Pointer to data.
 * \param _size_t	Size of data to be transfered.
 *
 * \return SPI Status Code.
 *
 */
static spi_status_code_t spi_write_start(const void *_ptrData, uint32_t _size_t);

/** \brief Start receive data from SPI peripheral.
 *
 * \param _pBuf			Pointer to buffer to store data.
 * \param _size_t		Size of data should be received.
 * \paran _pRecvSize	Pointer to data to store actual byte received.
 *
 * \return SPI Status Code.
 *
 */
static spi_status_code_t spi_read_start(void *_pBuf, uint32_t _size_t, uint32_t *_pRecvSize);


/** \brief Issue SPI received complete signal.
 *
 * \param _tmr	Pointer to timer;
 *
 */
static void prvRecvIssueSignal(void *_tmr);

/** \brief Select Chip to active.
 *
 * \param _spiPort		The port of SPI
 *
 */
static void prvSelectChip(SPI_port_t _spiPort);

/** \brief De-select Chip to inactive.
 *
 * \param _spiPort		The port of SPI
 *
 */
static void prvDeselectChip(SPI_port_t _spiPort);

/* ------------------------------------------------------------------------------------ */
/* SPI Interrupt. */
ISR(SPI_STC_vect)
{
	spi_interrupt_handler();
}


spi_status_code_t spi_open(SPI_port_t _xPort, const uint8_t _flag)
{
	spi_status_code_t _ret_status = SPI_STATUS_OK;

	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			/* Force close. */

			if(_flag == 2) {
				xSPISemaphore = true;
			}
			if(xSPISemaphore == true) {
				xSPISemaphore = false;
			}

			if(spi_is_master == true) {
				prvSelectChip(_xPort);
			}
		break;

		default:
			_ret_status = SPI_ERR_INVALID_ARG;
		break;
	}

	return _ret_status;
}


void spi_close(SPI_port_t _xPort)
{
	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			while(xSPISemaphore == false);
			xSPISemaphore = true;
			if(spi_is_master == true) {
				prvDeselectChip(_xPort);
			}
		break;

		default: break;
	}
}


void spi_master_init(void)
{
	/* Configure SPI GPIO. */
	gpio_configure_output(SPI_MOSI_GPIO);
	gpio_configure_output(SPI_CLK_GPIO);
	gpio_configure_output(SPI_MEGA162_CS_GPIO);
	gpio_configure_input(SPI_MISO_GPIO);
	gpio_configure_input(SPI_SS_GPIO);
	gpio_enable_pullup(SPI_MISO_GPIO);
	gpio_enable_pullup(SPI_SS_GPIO);
	gpio_set_pin_high(SPI_MEGA162_CS_GPIO);

	/* Initialize SPI as Master. */
	spi_is_master = true;
	spi_init();
}


void spi_slave_init(void)
{
	/* Configure SPI GPIO. */
	gpio_configure_input(SPI_MEGA162_CS_GPIO);
	gpio_configure_input(SPI_SS_GPIO);
	gpio_configure_input(SPI_MOSI_GPIO);
	gpio_configure_input(SPI_CLK_GPIO);
	gpio_configure_output(SPI_MISO_GPIO);
	gpio_enable_pullup(SPI_SS_GPIO);
	gpio_enable_pullup(SPI_MOSI_GPIO);
	gpio_enable_pullup(SPI_CLK_GPIO);
	gpio_enable_pullup(SPI_MEGA162_CS_GPIO);

	/* Initialize SPI as Slave. */
	spi_is_master = false;
	spi_init();
}


spi_status_code_t spi_write_byte(SPI_port_t _xPort, const void *_pPacket, uint32_t _size_t)
{
	spi_status_code_t _status = SPI_ERR_INVALID_ARG;

	/* Protect unpredicted behavior if argument is NULL pointer */
	if(_pPacket == NULL) return SPI_ERR_INVALID_ARG;

	/* Start send data. */
	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			_status = spi_write_start(_pPacket, _size_t);
		break;

		default: break;
	}

	return  _status;
}


spi_status_code_t spi_read_byte(SPI_port_t _xPort, void *_pRxBuff, uint32_t _size_t, uint32_t *_pRcvdSize)
{
	spi_status_code_t _status = SPI_ERR_INVALID_ARG;

	/* Protect unpredicted behavior if argument is NULL pointer */
	if(_pRxBuff == NULL) return SPI_ERR_INVALID_ARG;

	/* Start send data. */
	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			_status = spi_read_start(_pRxBuff, _size_t, _pRcvdSize);
		break;

		default: break;
	}

	return  _status;
}


void spi_setup(SPI_port_t _xPort, const spi_options_t *_opt)
{
	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			/* Setup Master/Slave Mode. */
			if(_opt->master == CONFIG_SPI_MASTER) {
				/* SPI is Master. */
				SPCR |= (1 << MSTR);
				spi_is_master = true;
			}
			else if (_opt->master == CONFIG_SPI_SLAVE){
				/* SPI is Slave. */
				SPCR &= ~(1 << MSTR);
				spi_is_master = false;
			}

			/* Setup Mode of SPI. */
			SPCR &= SPI_SELECT_MODE_MASK;
			switch(_opt->mode) {
				case CONFIG_SPI_MODE0:
					SPCR |= SPI_MODE0_BIT_MASK;
				break;

				case CONFIG_SPI_MODE1:
					SPCR |= SPI_MODE1_BIT_MASK;
				break;

				case CONFIG_SPI_MODE2:
					SPCR |= SPI_MODE2_BIT_MASK;
				break;

				case CONFIG_SPI_MODE3:
					SPCR |= SPI_MODE3_BIT_MASK;
				break;

				default: break;
			}

			/* Setup Transfer order. */
			switch(_opt->transfer_order) {
				case CONFIG_ORDER_MSB_FIRST:
					SPCR &= ~(1 << DORD);
				break;

				case CONFIG_ORDER_LSB_FIRST:
					SPCR |= (1 << DORD);
				break;

				default: break;
			}
		break;

		default: break;
	}
}


void spi_tranfer_mode(SPI_port_t _xPort, spi_transfer_mode_flag_t _flag)
{
	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			switch(_flag) {
				case SPI_POLLED:
					spi_interrupt_is_used = false;
				break;

				case SPI_INTERRUPT:
					spi_interrupt_is_used = true;
				break;

				default: break;
			}
		break;

		default: break;
	}
}


void spi_rx_timeout(SPI_port_t _xPort, const uint32_t _to_ms)
{

	switch(_xPort) {
		case serSPI1:
		case serSPI2:
		case serSPI3:
			if(_to_ms) {
				receive_timeout = _to_ms;
			}
		break;

		default: break;
	}
}


/* ------------------------------------------------------------------------------------ */
static void spi_interrupt_handler(void)
{
	uint8_t _status, _data;

	_status = SPSR;

	/* Check if any error. */
	if(_status & (1 << WCOL)) {
		/* Clear receiver buffer. */
		_data = SPDR;
	}
	/* Process data. */
	else {
		/* Copy register data. */
		_data = SPDR;

		/* Write operation. */
		if (operation_is_write == true) {
			/* No more data to transmit. */
			if(spi_tx_nb_count == 0) {
				/* Finish the transmit operation. */
				SPCR &= ~(1 << SPIE);
			}
			else {
				/* Send data to transmit data register. */
				SPDR = *spi_tx_data_byte;
				/* Increase the pointer of transmitter data buffer. */
				spi_tx_data_byte++;
				/* Decrease transmitted data number. */
				spi_tx_nb_count--;
			}
		}
		/* Read operation. */
		else {
			if(spi_rx_nb_count > 0) {
				/* Now, it is not waiting anymore. */
				spi_wait_recv_frame =  false;
				/* Decrease remaining number data to receive. */
				spi_rx_nb_count--;
				if(spi_rx_data_byte != NULL) {
					/* Copy data to receiver buffer. */
					*spi_rx_data_byte = _data;
					/* Increase pointer of receiver data buffer. */
					spi_rx_data_byte++;
					/* Increase received byte number. */
					spi_rx_nb_received++;

					if(spi_is_master == true) {
						/* Trigger next interrupt */
						SPDR = 0xFF;
					}
				}
			}
			else {
				/* Disable receive interrupt. */
				SPCR &= ~(1 << SPIE);
				/* Internal Signaling. */
				xSPISemaphore = true;
				/* Global Signaling. */
				xSPISignaling  = true;
			}
		}
	}
}


static void spi_init(void)
{
	/* Setup data order MSB first. */
	SPCR &= ~(1 << DORD);
	/* Default is SPI mode 0. */
	SPCR &= SPI_SELECT_MODE_MASK;
	SPCR |= SPI_MODE0_BIT_MASK;
	/* SPI Clock SCK/8 */
	SPCR &= 0xFC;
	SPCR |= (1 << SPR0);

	if(spi_is_master == true) {
		/* SPI is Master. */
		SPCR |= (1 << MSTR);
	}
	else {
		/* SPI is Slave. */
		SPCR &= ~(1 << MSTR);
	}

	/* Enable SPI. */
	SPCR |= (1 << SPE);

	if(_tmr_recv_spi == NULL) {
		_tmr_recv_spi = vTimer10msTake();
		if(_tmr_recv_spi == NULL) {
			while(1);
		}
	}
}

static spi_status_code_t spi_write_start(const void *_ptrData, uint32_t _size_t)
{
	/* Set operation to write transfer. */
	operation_is_write = true;
	/* Get the length to be transmitted. */
	spi_tx_nb_count = _size_t;
	/* Get the pointer of data. */
	spi_tx_data_byte = (const volatile uint8_t *)_ptrData;

	if (spi_interrupt_is_used == true) {
		/* Enable SPI Interrupt. */
		SPCR |= (1 << SPIE);
		/* Master Operation. */
		if(spi_is_master == true) {
			/* Transfer first byte to trigger interrupt */
			SPDR = *spi_tx_data_byte;
			/* Decrease transmitted data number. */
			spi_tx_nb_count--;
		}
	}
	else {
		/* Master and Slave Operation. */
		/* Disable SPI Interrupt. */
		SPCR &= ~(1 << SPIE);
		/* Transmit data until complete. */
		while(spi_tx_nb_count) {
			/* Send data. */
			SPDR = *spi_tx_data_byte;
			/* Increase the pointer of transmitter data buffer. */
			spi_tx_data_byte++;
			/* Decrease transmitted data number. */
			spi_tx_nb_count--;
			/* Wait for transmission complete. */
			while(!(SPSR & (1 << SPIF)));
		}
		/* Internal Signaling. */
		xSPISemaphore = true;
	}

	return SPI_STATUS_OK;
}


static spi_status_code_t spi_read_start(void *_pBuf, uint32_t _size_t, uint32_t *_pRecvSize)
{
	/* Set busy. */
	xSPISignaling = false;
	/* Set infinite for receiver timer. */
	*_tmr_recv_spi = 0xFFFFFFFF;
	/* Update receiving buffer */
	spi_rx_data_byte = (volatile uint8_t *)_pBuf;
	/* Set wait frame. */
	spi_wait_recv_frame = true;
	/* Clear number of received byte. */
	spi_rx_nb_received = 0;
	/* Assignment actual received byte. */
	_pRecvSize = (uint32_t *)&spi_rx_nb_received;
	/* Get number of byte to be received. */
	spi_rx_nb_count = _size_t;

	if (spi_interrupt_is_used == true) {
		/* Set receiver timeout. */
		*_tmr_recv_spi = receive_timeout;
		_funcRecvSignaling = &prvRecvIssueSignal;
		vTimer10msFuncReg(_funcRecvSignaling, (void *)_tmr_recv_spi);

		/* Enable SPI Interrupt. */
		SPCR |= (1 << SPIE);
		/* Master Operation. */
		if(spi_is_master == true) {
			/* Trigger interrupt */
			SPDR = 0xFF;
		}
	}
	else {
		/* Set infinite for receiver timer. */
		*_tmr_recv_spi = 0xFFFFFFFF;
		/* Disable SPI Interrupt. */
		SPCR &= ~(1 << SPIE);
		/* Wait data until complete. */
		while(spi_rx_nb_count && *_tmr_recv_spi) {
			/* Master Operation. */
			if(spi_is_master == true) {
				/* Write dummy */
				SPDR = 0xFF;
			}

			if(spi_rx_nb_received) {
				/* Update receive timeout. */
				*_tmr_recv_spi = receive_timeout;
			}

			/* Wait for transmission complete. */
			while(!(SPSR & (1 << SPIF)) && *_tmr_recv_spi);

			if(*_tmr_recv_spi) {
				/* Set receive timeout. */
				*_tmr_recv_spi = receive_timeout;
				/* Copy data to receiver buffer. */
				*spi_rx_data_byte = SPDR;
				/* Increase pointer of receiver data buffer. */
				spi_rx_data_byte++;
				/* Increase received byte number. */
				spi_rx_nb_received++;
				/* Decrease remaining number data to receive. */
				spi_rx_nb_count--;
			}
		}

		/* Now, it is not waiting anymore. */
		spi_wait_recv_frame =  false;
		/* Internal Signaling. */
		xSPISemaphore = true;
		xSPISignaling = true;
	}

	return SPI_STATUS_OK;
}


static void prvRecvIssueSignal(void *_tmr)
{
	uint32_t *_ptr = _tmr;

	if((*_ptr == 0) && (spi_wait_recv_frame == false)) {
		xSPISignaling = true;
		spi_wait_recv_frame = true;
	}
}


static void prvSelectChip(SPI_port_t _spiPort)
{
	switch(_spiPort) {
		case serSPI1:
			gpio_set_pin_low(SPI_MEGA162_CS_GPIO);
		break;

		case serSPI2:
			gpio_set_pin_low(SPI_MMC_CS_GPIO);
		break;

		case serSPI3:
		break;

		default: break;
	}
}


static void prvDeselectChip(SPI_port_t _spiPort)
{
	switch(_spiPort) {
		case serSPI1:
			gpio_set_pin_high(SPI_MEGA162_CS_GPIO);
		break;

		case serSPI2:
			gpio_set_pin_high(SPI_MMC_CS_GPIO);
		break;

		case serSPI3:
		break;

		default: break;
	}
}



#endif /* BOARD_2_1_USED */
